/**************************************************************************
* Tint2 : system (resource) monitor
*
* Copyright (C) 2010  mrovi@interfete-web-club.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**************************************************************************/

#include <string.h>
#include <stdio.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <pango/pangocairo.h>
#include <sys/file.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <math.h>
#include <dirent.h>

#include "window.h"
#include "server.h"
#include "area.h"
#include "panel.h"
#include "taskbar.h"
#include "sysmon.h"

int sysmon_enabled;
static timeout* sysmon_timeout;

#define COLOR_TO_BYTE(C) (int)(C.color[0]*255+0.5), (int)(C.color[1]*255+0.5), (int)(C.color[2]*255+0.5), (int)(C.alpha*255+0.5)
#define COLOR(C) C.color[0], C.color[1], C.color[2], C.alpha

void update_sysmon_sec(void* arg);
void sysmon_tick(void *obj);
void sysmon_redraw_graphs(void *obj);
void get_cpu_num(Sysmon *sysmon);

void default_sysmon()
{
	sysmon_enabled = 0;
	sysmon_timeout = 0;
}

void init_sysmon()
{
}

void init_sysmon_panel(void *p)
{
	Panel *panel =(Panel*)p;
	Sysmon *sysmon = &panel->sysmon;

	if (!sysmon_enabled)
		return;

	sysmon->area.parent = p;
	sysmon->area.panel = p;
	sysmon->area._draw_foreground = draw_sysmon;
	sysmon->area.size_mode = SIZE_BY_CONTENT;
	sysmon->area._resize = resize_sysmon;
	sysmon->area.resize = 1;
	sysmon->area.redraw = 1;
	sysmon->area.on_screen = 1;
	if (sysmon->area.bg == 0)
		sysmon->area.bg = &g_array_index(backgrounds, Background, 0);

	if (panel_horizontal) {
		// panel horizonal => fixed height and posy
		//sysmon->area.posy = panel->area.bg->border.width + panel->area.paddingy;
		sysmon->area.height = panel->area.height - (2 * sysmon->area.posy);
	} else {
		// panel vertical => fixed width, height, posy and posx
		//sysmon->area.posx = panel->area.bg->border.width + panel->area.paddingxlr;
		sysmon->area.width = panel->area.width - (2 * panel->area.bg->border.width) - (2 * panel->area.paddingy);
	}

	// internal stuff here
	get_cpu_num(sysmon);
	sysmon->ngraphs = (sysmon->cpu_enabled ? sysmon->nphycores : 0) + (sysmon->mem_enabled ? 1 : 0) + (sysmon->net_enabled ? 1 : 0);

	// create graphs
	if (sysmon->cpu_enabled) {
		int i;
		for (i = 0; i < sysmon->nphycores; i++) {
			SysmonGraph *g = calloc(1, sizeof(SysmonGraph));
			g->max = 1.0;
			g->width = panel_horizontal ? sysmon->graph_size : 100;
			g->height = panel_horizontal ? 100 : sysmon->graph_size;
			g->history = calloc(g->width, sizeof(double));
			g->image = imlib_create_image(g->width, g->height);
			sprintf(g->id, "cpu%d", i);
			sysmon->graphs = g_slist_append(sysmon->graphs, g);
		}
	}

	if (sysmon->mem_enabled) {
		SysmonGraph *g = calloc(1, sizeof(SysmonGraph));
		g->max = 1.0;
		g->width = panel_horizontal ? sysmon->graph_size : 100;
		g->height = panel_horizontal ? 100 : sysmon->graph_size;
		g->history = calloc(g->width, sizeof(double));
		g->image = imlib_create_image(g->width, g->height);
		sprintf(g->id, "mem");
		sysmon->graphs = g_slist_append(sysmon->graphs, g);
	}

	if (sysmon->net_enabled) {
		SysmonGraph *g = calloc(1, sizeof(SysmonGraph));
		g->max = 1.0;
		g->width = panel_horizontal ? sysmon->graph_size : 100;
		g->height = panel_horizontal ? 100 : sysmon->graph_size;
		g->history = calloc(g->width, sizeof(double));
		g->image = imlib_create_image(g->width, g->height);
		sprintf(g->id, "net");
		sysmon->graphs = g_slist_append(sysmon->graphs, g);
	}

	sysmon_timeout = add_timeout(10, 1000, update_sysmon_sec, 0);

	panel_refresh = 1;
}

void cleanup_sysmon()
{
	int i;

	for (i = 0 ; i < nb_panel ; i++) {
		Panel *panel = &panel1[i];
		Sysmon *sysmon = &panel->sysmon;
		free_area(&sysmon->area);
		GSList *l;
		for (l = sysmon->graphs; l ; l = l->next) {
			SysmonGraph *g = (SysmonGraph *)l->data;
			if (g->image) {
				imlib_context_set_image(g->image);
				imlib_free_image();
			}
			free(g->history);
			free(g);
		}
		g_slist_free(sysmon->graphs);
		if (sysmon->sysmon_font_descr) pango_font_description_free(sysmon->sysmon_font_descr);
		free(sysmon->cpu2phycore);
		free(sysmon->num_cores_per_cpu);
	}
	sysmon_enabled = 0;
}

int resize_sysmon(void *obj)
{
	Sysmon *sysmon = obj;
	Panel *panel = sysmon->area.panel;
	GSList *l;
	int g_w, g_h;

	int nrows, ncols;
	if (panel_horizontal) {
		sysmon->area.height = panel->area.height - (2 * panel->area.bg->border.width) - (2 * panel->area.paddingy);

		ncols = sysmon->ngraphs;
		nrows = 1;

		sysmon->area.width = (2 * sysmon->area.bg->border.width) + (2 * sysmon->area.paddingxlr) +
				(sysmon->graph_size * ncols) + ((nrows-1) * sysmon->area.paddingx);

		/*sysmon->area.posx = panel->area.bg->border.width + panel->area.paddingxlr;
		sysmon->area.posy = panel->area.bg->border.width;
		if (panel->launcher.area.on_screen && panel->launcher.area.width)
			sysmon->area.posx += (panel->launcher.area.width + panel->area.paddingx);
		*/

		g_w = sysmon->graph_size;
		g_h = sysmon->area.height - 2 *(sysmon->area.bg->border.width + sysmon->area.paddingy);
	} else {
		sysmon->area.width = panel->area.width - (2 * panel->area.bg->border.width) - (2 * panel->area.paddingy);

		ncols = sysmon->graphs_per_row;
		nrows = sysmon->ngraphs / sysmon->graphs_per_row + (sysmon->ngraphs % sysmon->graphs_per_row ? 1 : 0);

		sysmon->area.height = (2 * sysmon->area.bg->border.width) + (2 * sysmon->area.paddingxlr) +
				(sysmon->graph_size * nrows) + ((nrows-1) * sysmon->area.paddingx);

		/*sysmon->area.posx = panel->area.bg->border.width;
		sysmon->area.posy = panel->area.height - panel->area.bg->border.width - panel->area.paddingxlr - sysmon->area.height;
		if (panel->launcher.area.on_screen && panel->launcher.area.height)
			sysmon->area.posy -= (panel->launcher.area.height + panel->area.paddingx);
		*/

		g_h = sysmon->graph_size;
		g_w = (sysmon->area.width - 2 *(sysmon->area.bg->border.width + sysmon->area.paddingy) - (ncols-1) * sysmon->area.paddingx) / ncols;
	}

	// Resize graphs if necessary
	int resized = 0;
	for (l = sysmon->graphs; l ; l = l->next) {
		SysmonGraph *g = (SysmonGraph *)l->data;

		if (g->width != g_w || g->height != g_h) {
			resized = 1;
			g->width = g_w;
			g->height = g_h;

			imlib_context_set_image(g->image);
			imlib_free_image();
			g->image = imlib_create_image(g->width, g->height);

			g->history = realloc(g->history, g->width * sizeof(double));
		}
	}
	if (resized)
		sysmon_redraw_graphs(sysmon);

	// Position graphs
	int i, posx, posy;
	int start = sysmon->area.bg->border.width + sysmon->area.paddingy;
	if (panel_horizontal) {
		posy = start;
		posx = sysmon->area.bg->border.width + sysmon->area.paddingxlr;
	}
	else {
		posx = start;
		posy = sysmon->area.bg->border.width + sysmon->area.paddingxlr;
	}

	for (i = 1, l = sysmon->graphs; l ; i++, l = l->next) {
		SysmonGraph *g = (SysmonGraph*)l->data;

		g->y = posy;
		g->x = posx;
		if (panel_horizontal) {
			if (i % nrows)
				posy += g->height + sysmon->area.paddingx;
			else {
				posy = start;
				posx += (g->width + sysmon->area.paddingx);
			}
		}
		else {
			if (i % ncols)
				posx += g->width + sysmon->area.paddingx;
			else {
				posx = start;
				posy += (g->height + sysmon->area.paddingx);
			}
		}
	}

	// resize force the redraw
	// sysmon->area.redraw = 1;
	return 1;
}

void draw_text(cairo_t *cairo, PangoFontDescription *font, int x, int y, int w, int align, Color color, char *s, int len)
{
	PangoLayout *layout;
	layout = pango_cairo_create_layout(cairo);

	// draw layout
	pango_layout_set_font_description(layout, font);
	pango_layout_set_width(layout, w * PANGO_SCALE);
	pango_layout_set_alignment(layout, align);
	pango_layout_set_text(layout, s, len);

	int shadow_size = 3;
	double shadow_edge_alpha = 0.2;
	int i, j;
	for (i = -shadow_size; i <= shadow_size; i++) {
		for (j = -shadow_size; j <= shadow_size; j++) {
			cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 1.0 - (1.0 - shadow_edge_alpha) * (i*i + j*j)/(double)(shadow_size*shadow_size));
			pango_cairo_update_layout(cairo, layout);
			cairo_move_to(cairo, x + i, y + j);
			pango_cairo_show_layout(cairo, layout);
		}
	}

	cairo_set_source_rgba(cairo, COLOR(color));
	pango_cairo_update_layout(cairo, layout);
	cairo_move_to(cairo, x, y);
	pango_cairo_show_layout(cairo, layout);

	g_object_unref(layout);
}

void draw_sysmon(void *obj, cairo_t *c)
{
	Sysmon *sysmon = obj;
	GSList *l;

	if (sysmon->graphs == NULL)
		return;

	for (l = sysmon->graphs; l ; l = l->next) {
		SysmonGraph *g = (SysmonGraph *)l->data;

		int pos_x = g->x;
		int pos_y = g->y;

		// Render
		imlib_context_set_image(g->image);
		if (server.real_transparency) {
			render_image(sysmon->area.pix, pos_x, pos_y, imlib_image_get_width(), imlib_image_get_height());
		} else {
			imlib_context_set_drawable(sysmon->area.pix);
			imlib_render_image_on_drawable(pos_x, pos_y);
		}

		// Draw text
		if (*g->text_topleft) {
			Color color;
			color.color[0] = color.color[1] = color.color[2] = color.alpha = 1.0;
			if (strstr(g->id, "cpu") == g->id) {
				color = sysmon->cpu_font_color;
			} else if (strstr(g->id, "mem") == g->id) {
				color = sysmon->mem_font_color;
			} else if (strstr(g->id, "net") == g->id) {
				color = sysmon->net_font_color;
			}
			draw_text(c, sysmon->sysmon_font_descr, g->x, g->y, g->width, PANGO_ALIGN_LEFT, color, g->text_topleft, strlen(g->text_topleft));
		}
		if (*g->text_topright) {
			draw_text(c, sysmon->sysmon_font_descr, g->x, g->y, g->width, PANGO_ALIGN_RIGHT, g->topright_color, g->text_topright, strlen(g->text_topright));
		}
	}
}

void update_sysmon_sec(void* arg)
{
	if (!sysmon_enabled)
		return;

	int i;
	for (i = 0; i < nb_panel; i++)
		sysmon_tick(&panel1[i].sysmon);

	panel_refresh = 1;
}

void get_cpu_num(Sysmon *sysmon)
{
	char line[4096];
	int num_virtual = 0;
	int num_physical_cpus = 0;
	int num_physical_cores = 0;

	FILE *f = fopen("/proc/cpuinfo", "rt");
	if (f) {
		// first pass: find virtual cpu count and physical cpu count
		while (fgets(line, sizeof(line), f) != NULL) {
			if (strstr(line, ":") == NULL)
				continue;
			char *value = strstr(line, ":") + 1;
			if (strstr(line, "processor") == line) {
				num_virtual++;
			} else if (strstr(line, "physical id") == line) {
				int x = 0;
				sscanf(value, "%d", &x);
				if (x + 1 > num_physical_cpus) {
					num_physical_cpus = x + 1;
				}
			}
		}
		fseek(f, 0, SEEK_SET);

		if (num_virtual) {
			sysmon->cpu2phycore = (int*)calloc(sizeof(int), num_virtual);
		} else {
			sysmon->cpu2phycore = (int*)calloc(sizeof(int), 1);
			sysmon->num_cores_per_cpu = (int*)calloc(sizeof(int), 1);
			sysmon->num_cores_per_cpu[0] = 1;
		}

		if (num_virtual && num_physical_cpus) {
			// second pass: find number of real cpu cores for each real cpu
			sysmon->num_cores_per_cpu = (int*)calloc(sizeof(int), num_physical_cpus);

			int in_processor = 0;
			int virtual_id = -1;
			int physical_id = -1;
			int core_id = -1;
			int cpu_cores = -1;
			int siblings = -1;
			while (fgets(line, sizeof(line), f) != NULL) {
				if (strstr(line, "processor") == line) {
					in_processor = 1;
					physical_id = -1;
					core_id = -1;
					cpu_cores = -1;
					virtual_id = -1;
				}
				if (strstr(line, ":") == NULL) {
					in_processor = 0;
					if (virtual_id >= 0 && physical_id >= 0 && core_id >= 0 &&
						cpu_cores >= 0 && siblings >= 0) {
						// save it
						sysmon->num_cores_per_cpu[physical_id] = cpu_cores;
					}
				}
				if (in_processor) {
					char *value = strstr(line, ":") + 1;
					if (strstr(line, "processor") == line) {
						sscanf(value, "%d", &virtual_id);
					} else if (strstr(line, "physical id") == line) {
						sscanf(value, "%d", &physical_id);
					} else if (strstr(line, "core id") == line) {
						sscanf(value, "%d", &core_id);
					} else if (strstr(line, "cpu cores") == line) {
						sscanf(value, "%d", &cpu_cores);
					} else if (strstr(line, "siblings") == line) {
						sscanf(value, "%d", &siblings);
					}
				}
			}
			fseek(f, 0, SEEK_SET);

			int i;
			num_physical_cores = 0;
			for (i = 0; i < num_physical_cpus; ++i) {
				num_physical_cores += sysmon->num_cores_per_cpu[i];
			}

			// third pass: map virtual cpu id to real core id
			in_processor = 0;
			virtual_id = -1;
			physical_id = -1;
			core_id = -1;
			cpu_cores = -1;
			siblings = -1;
			while (fgets(line, sizeof(line), f) != NULL) {
				if (strstr(line, "processor") == line) {
					in_processor = 1;
					physical_id = -1;
					core_id = -1;
					cpu_cores = -1;
					virtual_id = -1;
				}
				if (strstr(line, ":") == NULL) {
					in_processor = 0;
					if (virtual_id >= 0 && physical_id >= 0 && core_id >= 0 &&
							cpu_cores >= 0 && siblings >= 0) {
						// save it
						// physical core id = sum of num of real cores of each cpu
						// with physical id < current cpu physical id +
						// core id
						int actual_core_id = core_id;
						for (i = 0; i < physical_id; ++i) {
							actual_core_id += sysmon->num_cores_per_cpu[i];
						}
						sysmon->cpu2phycore[virtual_id] = actual_core_id;
					}
				}
				if (in_processor) {
					char *value = strstr(line, ":") + 1;
					if (strstr(line, "processor") == line) {
						sscanf(value, "%d", &virtual_id);
					} else if (strstr(line, "physical id") == line) {
						sscanf(value, "%d", &physical_id);
					} else if (strstr(line, "core id") == line) {
						sscanf(value, "%d", &core_id);
					} else if (strstr(line, "cpu cores") == line) {
						sscanf(value, "%d", &cpu_cores);
					} else if (strstr(line, "siblings") == line) {
						sscanf(value, "%d", &siblings);
					}
				}
			}
			fseek(f, 0, SEEK_SET);
		}

		fclose(f);
	} else {
		sysmon->cpu2phycore = (int*)calloc(sizeof(int), 1);
		sysmon->num_cores_per_cpu = (int*)calloc(sizeof(int), 1);
		sysmon->num_cores_per_cpu[0] = 1;
	}

//	int i;
//	for (i = 0; i < num_virtual; ++i) {
//		fprintf(stderr, "Virtual cpu %d is real core %d\n", i, sysmon->cpu2phycore[i]);
//	}

	sysmon->ncpus = num_virtual;
	sysmon->nphycores = num_physical_cores;
	sysmon->nphycpus = num_physical_cpus;
}

double MIN3(double r, double g, double b) {
	if (r < g) {
		if (r < b) {
			return r;
		} else {
			return b;
		}
	} else {
		if (g < b) {
			return g;
		} else {
			return b;
		}
	}
}

double MAX3(double r, double g, double b) {
	if (r > g) {
		if (r > b) {
			return r;
		} else {
			return b;
		}
	} else {
		if (g > b) {
			return g;
		} else {
			return b;
		}
	}
}

void RGBtoHSV( double r, double g, double b, double a_rgb, double *h, double *s, double *v, double *a_hsv)
{
	*a_hsv = a_rgb;
	double min, max, delta;
	min = MIN3( r, g, b );
	max = MAX3( r, g, b );
	*v = max;				// v
	delta = max - min;
	if( max != 0 )
		*s = delta / max;		// s
	else {
		// r = g = b = 0		// s = 0, v is undefined
		*s = 0;
		*h = -1;
		return;
	}
	if( r == max )
		*h = ( g - b ) / delta;		// between yellow & magenta
	else if( g == max )
		*h = 2 + ( b - r ) / delta;	// between cyan & yellow
	else
		*h = 4 + ( r - g ) / delta;	// between magenta & cyan
	*h *= 60;				// degrees
	if( *h < 0 )
		*h += 360;
}

void HSVtoRGB(double *r, double *g, double *b, double *a_rgb, double h, double s, double v, double a_hsv)
{
	*a_rgb = a_hsv;
	int i;
	double f, p, q, t;
	if( s == 0 ) {
		// achromatic (grey)
		*r = *g = *b = v;
		return;
	}
	h /= 60;			// sector 0 to 5
	i = floor( h );
	f = h - i;			// factorial part of h
	p = v * ( 1 - s );
	q = v * ( 1 - s * f );
	t = v * ( 1 - s * ( 1 - f ) );
	switch( i ) {
		case 0:
			*r = v;
			*g = t;
			*b = p;
			break;
		case 1:
			*r = q;
			*g = v;
			*b = p;
			break;
		case 2:
			*r = p;
			*g = v;
			*b = t;
			break;
		case 3:
			*r = p;
			*g = q;
			*b = v;
			break;
		case 4:
			*r = t;
			*g = p;
			*b = v;
			break;
		default:		// case 5:
			*r = v;
			*g = p;
			*b = q;
			break;
	}
}

Color interpolate_colors(Color c1, double l1, Color c2, double l2, double x)
{
	if (x <= l1)
		return c1;
	if (x >= l2)
		return c2;
	if (l1 == l2)
		return c1;

	Color hsv1, hsv2;

	RGBtoHSV(c1.color[0], c1.color[1], c1.color[2], c1.alpha, &hsv1.color[0], &hsv1.color[1], &hsv1.color[2], &hsv1.alpha);
	RGBtoHSV(c2.color[0], c2.color[1], c2.color[2], c2.alpha, &hsv2.color[0], &hsv2.color[1], &hsv2.color[2], &hsv2.alpha);

	Color hsv, c;
	hsv.color[0] = hsv1.color[0] + (x-l1)/(l2-l1) * (hsv2.color[0] - hsv1.color[0]);
	hsv.color[1] = hsv1.color[1] + (x-l1)/(l2-l1) * (hsv2.color[1] - hsv1.color[1]);
	hsv.color[2] = hsv1.color[2] + (x-l1)/(l2-l1) * (hsv2.color[2] - hsv1.color[2]);
	hsv.alpha = hsv1.alpha + (x-l1)/(l2-l1) * (hsv2.alpha - hsv1.alpha);

	HSVtoRGB(&c.color[0], &c.color[1], &c.color[2], &c.alpha, hsv.color[0], hsv.color[1], hsv.color[2], hsv.alpha);
	return c;
}

void sysmon_tick(void *obj)
{
	Sysmon *sysmon = obj;
	char line[4096];
	double cpu_total[4096];
	double cpu_idle[4096];
	double cpu_freq[4096];
	double cpu_temp[4096];
	double cpu_scaling[4096];
	double ram_usage;
	int ram_usage_mb;
	double rx = 0;
	double tx = 0;
	FILE *f;

	if (sysmon->cpu_enabled) {
		f = fopen("/proc/stat", "rt");
		if (f) {
			int i;
			for (i = 0; i < sysmon->nphycores; ++i) {
				cpu_total[i] = cpu_idle[i] = 0;
			}
			while (fgets(line, sizeof(line), f) != NULL) {
				if (strstr(line, "cpu ") == line)
					continue;
				if (strstr(line, "cpu") != line)
					continue;
				int cpuindex = -1;
				if (sscanf(line + 3, "%d", &cpuindex) != 1)
					continue;
				if (cpuindex >= 0 && cpuindex < sysmon->ncpus) {
					double user, nice, system, idle, wait, irq, softirq;
					sscanf(line, "%*s%lf%lf%lf%lf%lf%lf%lf", &user, &nice, &system, &idle, &wait, &irq, &softirq);
					cpu_total[sysmon->cpu2phycore[cpuindex]] += user + nice + system + idle + wait + irq + softirq;
					cpu_idle[sysmon->cpu2phycore[cpuindex]] += idle;
				}
			}
			fclose(f);
		}

		{
			int i_phy_cpu;
			for (i_phy_cpu = 0; i_phy_cpu < sysmon->nphycpus; ++i_phy_cpu) {
				char buf[256];
				sprintf(buf, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i_phy_cpu);
				f = fopen(buf, "rt");
				if (!f) {
					sprintf(buf, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", i_phy_cpu);
					f = fopen(buf, "rt");
				}
				if (f) {
					double cur_freq;
					fscanf(f, "%lf", &cur_freq);
					int i;
					int j = 0;
					for (i = 0; i < i_phy_cpu; i++) {
						j += sysmon->num_cores_per_cpu[i];
					}
					int k;
					for (k = 0; k < sysmon->num_cores_per_cpu[i_phy_cpu]; k++, j++) {
						cpu_freq[j] = cur_freq;
					}
					fclose(f);
				}
			}
		}

		{
			int i_phy_cpu;
			for (i_phy_cpu = 0; i_phy_cpu < sysmon->nphycpus; ++i_phy_cpu) {
				double max_freq = -1;
				char buf[256];
				sprintf(buf, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", i_phy_cpu);
				f = fopen(buf, "rt");
				if (f) {
					fscanf(f, "%lf", &max_freq);
					int i;
					int j = 0;
					for (i = 0; i < i_phy_cpu; i++) {
						j += sysmon->num_cores_per_cpu[i];
					}
					int k;
					for (k = 0; k < sysmon->num_cores_per_cpu[i_phy_cpu]; k++, j++) {
						if (cpu_freq[j] > 0 && max_freq > 0) {
							cpu_scaling[j] = cpu_freq[j] / max_freq;
						} else {
							cpu_scaling[j] = -1;
						}
					}
					fclose(f);
				}
			}
		}

		{
			int i;
			for (i = 0; i < sysmon->ncpus; i++) {
				cpu_temp[sysmon->cpu2phycore[i]] = -1000;
			}
		}

		int i_phy_cpu;
		for (i_phy_cpu = 0; i_phy_cpu < sysmon->nphycpus; i_phy_cpu++) {
			char buf[256];
			sprintf(buf, "/sys/devices/platform/coretemp.%d", i_phy_cpu);
			struct stat st;
			if (stat(buf, &st) == 0) {
				// dir exists
				int i;
				for (i = 1; 1; ++i) {
					sprintf(buf, "/sys/devices/platform/coretemp.%d/temp%d_label", i_phy_cpu, i);
					FILE *flabel = fopen(buf, "rt");
					if (!flabel)
						break;
					if (fgets(line, sizeof(line), flabel) != NULL) {
						if (strstr(line, "Core ") == line) {
							int i_phy_core = -1;
							sscanf(line, "%*s%d", &i_phy_core);
							if (i_phy_core >= 0 && i_phy_core < sysmon->num_cores_per_cpu[i_phy_cpu]) {
								sprintf(buf, "/sys/devices/platform/coretemp.%d/temp%d_input", i_phy_cpu, i);
								FILE *finput = fopen(buf, "rt");
								if (finput) {
									if (fgets(line, sizeof(line), finput) != NULL) {
										double v;
										if (sscanf(line, "%lf", &v) == 1) {
											v /= 1000.0;
											int i_core = 0;
											int j;
											for (j = 0; j < i_phy_cpu; ++j) {
												i_core += sysmon->num_cores_per_cpu[j];
											}
											i_core += i_phy_core;
											if (v > cpu_temp[i_core])
												cpu_temp[i_core] = v;
										}
									}
									fclose(finput);
								}
							}
						}
					}
					fclose(flabel);
				}
            } else {
                sprintf(buf, "/sys/bus/pci/drivers/k8temp");
                if (stat(buf, &st) == 0) {
                    // exists
                    DIR *dir = opendir(buf);
                    if (dir) {
                        struct dirent *ent;
                        for (ent = readdir(dir); ent; ent = readdir(dir)) {
                            int i_cpu = 0;
                            for (i_cpu = 0; i_cpu < sysmon->ncpus; i_cpu++) {
                                sprintf(buf, "/sys/bus/pci/drivers/k8temp/%s/temp%d_input",
                                        ent->d_name,
                                        i_cpu * 2 + 1);
                                FILE *finput = fopen(buf, "rt");
                                if (finput) {
                                    if (fgets(line, sizeof(line), finput) != NULL) {
                                        double v;
                                        if (sscanf(line, "%lf", &v) == 1) {
                                            v /= 1000.0;
                                            if (v > cpu_temp[i_cpu])
                                                cpu_temp[i_cpu] = v;
                                        }
                                    }
                                    fclose(finput);
                                }
                            }
                        }
                        closedir(dir);
                    }
                } else {
                    sprintf(buf, "/sys/devices/virtual/thermal/thermal_zone0/temp");
                    FILE *finput = fopen(buf, "rt");
                    if (finput) {
                        if (fgets(line, sizeof(line), finput) != NULL) {
                            double v;
                            if (sscanf(line, "%lf", &v) == 1) {
                                v /= 1000.0;
                                int i;
                                for (i = 0; i < sysmon->ncpus; i++) {
                                    cpu_temp[sysmon->cpu2phycore[i]] = v;
                                }
                            }
                        }
                        fclose(finput);
                    }
                }
            }
		}
	}

	if (sysmon->mem_enabled) {
		f = fopen("/proc/meminfo", "rt");
		if (f) {
			double total, free, buffers, cache;
			fscanf(f, "%*s%lf%*s%*s%lf%*s%*s%lf%*s%*s%lf%*s", &total, &free, &buffers, &cache);
			fclose(f);

			ram_usage = (total - free - buffers - cache) / total;
			ram_usage_mb = (int) ((total - free - buffers - cache) / 1024 + 0.5);
		}
	}

	if (sysmon->net_enabled) {
		f = fopen("/proc/net/dev", "rt");
		if (f) {
			int index = 0;
			while (fgets(line, sizeof(line), f) != NULL) {
				if (index >= 2) {
					double irx, itx;
					char *p = line;
					while (*p != ':' && *p)
						p++;
					if (*p == ':')
						p++;
					sscanf(p, "%lf%*s%*s%*s%*s%*s%*s%*s%lf", &irx, &itx);
					rx += irx;
					tx += itx;
				}
				index++;
			}
			fclose(f);
		}
	}

	GSList *l;
	for (l = sysmon->graphs; l ; l = l->next) {
		SysmonGraph *g = (SysmonGraph *)l->data;
		int i;

		if (sysmon->cpu_enabled && strstr(g->id, "cpu") == g->id) {
			int cpuindex;
			sscanf(g->id + strlen("cpu"), "%d", &cpuindex);

			for (i = g->width - 1; i > 0; i--)
				g->history[i] = g->history[i-1];

			{
				double total_diff = g->old1 - cpu_total[cpuindex];
				double idle_diff = g->old2 - cpu_idle[cpuindex];
				g->old1 = cpu_total[cpuindex];
				g->old2 = cpu_idle[cpuindex];
				g->history[0] = total_diff == 0 ? 0 : 1 - idle_diff / total_diff;
			}
			if (cpu_freq[cpuindex] > 1) {
				sprintf(g->text_topleft, "%3.0f", round(cpu_freq[cpuindex] / 1.0e3 / 1.0e2) * 1.0e2);
			} else {
				sprintf(g->text_topleft, "%s", g->id);
			}
			if (cpu_temp[cpuindex] > 0) {
				sprintf(g->text_topright, "%2.0f C", cpu_temp[cpuindex]);
				Color green;
				green.color[0] = 0; green.color[1] = 1; green.color[2] = 0; green.alpha = 1;
				Color red;
				red.color[0] = 1; red.color[1] = 0; red.color[2] = 0; red.alpha = 1;
				g->topright_color = interpolate_colors(green, 20.0, red, 90.0, cpu_temp[cpuindex]);
			} else {
				g->text_topright[0] = '\0';
			}

			g->shade = cpu_scaling[cpuindex];
			g->updates++;
		} else if (sysmon->mem_enabled && strstr(g->id, "mem") == g->id) {
			for (i = g->width - 1; i > 0; i--)
				g->history[i] = g->history[i-1];
			g->history[0] = ram_usage;
			sprintf(g->text_topleft, "%d MB", ram_usage_mb);
			g->updates++;
		} else if (sysmon->net_enabled && strstr(g->id, "net") == g->id) {
			if (g->updates == 0) {
				g->old1 = rx;
				g->old2 = tx;
			} else if (g->updates % 5 == 0) {
				double rx_diff = rx - g->old1;
				double tx_diff = tx - g->old2;
				double x_diff = rx_diff + tx_diff;
				g->old1 = rx;
				g->old2 = tx;

				for (i = g->width - 1; i > 0; i--)
					g->history[i] = g->history[i-1];
				g->history[0] = x_diff;

				g->max = 0;
				int mbps = 0;
				for (i = g->width - 1; i >= 0; i--) {
					while (g->history[i] > g->max) {
						g->max += 131072 * 5; // 1Mb
						mbps++;
					}
				}
				sprintf(g->text_topleft, "%d Mbps", mbps);
			}
			g->updates++;
		}
	}

	sysmon_redraw_graphs(sysmon);
}

void sysmon_redraw_graphs(void *obj)
{
	Sysmon *sysmon = obj;
	GSList *l;
	for (l = sysmon->graphs; l ; l = l->next) {
		SysmonGraph *g = (SysmonGraph *)l->data;
		int i;

		// Draw bg
		imlib_context_set_image(g->image);
		imlib_context_set_blend(0);
		imlib_image_set_has_alpha(1);
		imlib_context_set_color(1, 1, 1, 0);
		imlib_image_fill_rectangle(0, 0, g->width, g->height);
		imlib_context_set_blend(1);
		imlib_context_set_color(COLOR_TO_BYTE(sysmon->graph_bg_color));
		imlib_image_fill_rectangle(0, 0, g->width, g->height);

		if (strstr(g->id, "cpu") == g->id && g->shade > 0) {
			int h = (int) (g->height * g->shade + 0.5);
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->cpu_freq_color));
			while (h > g->height) {
				imlib_image_fill_rectangle(0, 0, g->width, g->height);
				h -= g->height;
			}
			imlib_image_fill_rectangle(0, g->height - h, g->width, h);
		}

		// Draw bars
		Imlib_Color_Range range = imlib_create_color_range();
		imlib_context_set_color_range(range);

		if (strstr(g->id, "cpu") == g->id) {
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->cpu_color_end));
			imlib_add_color_to_color_range(0);
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->cpu_color_start));
			imlib_add_color_to_color_range(g->height);
		} else if (strstr(g->id, "mem") == g->id) {
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->mem_color_end));
			imlib_add_color_to_color_range(0);
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->mem_color_start));
			imlib_add_color_to_color_range(g->height);
		} else if (strstr(g->id, "net") == g->id) {
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->net_color_end));
			imlib_add_color_to_color_range(0);
			imlib_context_set_color(COLOR_TO_BYTE(sysmon->net_color_start));
			imlib_add_color_to_color_range(g->height);
		} else {
			imlib_free_color_range();
			continue;
		}

		for (i = 0; i < g->width; i++) {
			if (g->max < 0.001)
				continue;
			if (g->history[i] > 0.1 + g->max)
				g->history[i] = g->max;

			int h = (int) (g->height * g->history[i] / g->max + 0.5);
			if (h < 0 || h > g->height)
				continue;
			imlib_image_fill_color_range_rectangle(g->width - i - 1, g->height - h, 1, h, 0.0);
		}
		imlib_free_color_range();
	}

	sysmon->area.redraw = 1;
}
